<?php

namespace app\service;

use app\model\Form;
use app\model\Node;
use app\validate\FormValidate;
use table\ParseConf;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Db;

class FormService
{
    protected $table = [
        'dept' => 1,
        'dept_leader' => 1,
        'dict' => 1,
        'dict_cate' => 1,
        'form' => 1,
        'node' => 1,
        'role' => 1,
        'user' => 1,
    ];

    /**
     * @Inject()
     * @var Form
     */
    protected $formModel;

    /**
     * @Inject()
     * @var Node
     */
    protected $nodeModel;

    /**
     * 获取表单列表
     * @param $param
     * @return array
     */
    public function getFormList($param)
    {
        $limit = $param['pageSize'];

        $where = [];
        if (!empty($param['title'])) {
            $where[] = ['title', 'like', '%' . $param['title'] . '%'];
        }

        return $this->formModel->getFormList($limit, $where);
    }

    /**
     * 添加表单
     * @param $param
     * @return array
     */
    public function addForm($param)
    {
        try {

            validate(FormValidate::class)->check($param);
        } catch (ValidateException $e) {
            return dataReturn(-1, $e->getError());
        }

        // 物理上检测表是否存在
        $hasTable = Db::query('SHOW TABLES LIKE "' . makeTable($param['table'] . '_auto') . '"');
        if (!empty($hasTable)) {
            return dataReturn(-3, '该数据表已存在');
        }

        if (isset($this->table[strtolower($param['table'])])) {
            return dataReturn(-4, '该数据表已存在');
        }

        return $this->formModel->addForm($param);
    }

    /**
     * 编辑表单
     * @param $param
     * @return array
     */
    public function editForm($param)
    {
        try {

            validate(FormValidate::class)->check($param);
        } catch (ValidateException $e) {
            return dataReturn(-1, $e->getError());
        }

        // 物理上检测表是否存在
        $oldTableName = $this->formModel->getInfoById($param['id'])['data'];
        $hasTable = Db::query('SHOW TABLES LIKE "' . makeTable($param['table'] . '_auto'). '"');
        if (!empty($hasTable)  && $oldTableName['table'] != $param['table']) {
            return dataReturn(-3, '该数据表已存在');
        }

        if (isset($this->table[strtolower($param['table'])])) {
            return dataReturn(-4, '该数据表已存在');
        }

        return $this->formModel->editForm($param);
    }

    public function deploy($param)
    {
        $id = $param['id'];
        $type = $param['type']; // type = 1 覆盖部署 type = 2 增量部署

        $info = $this->formModel->getInfoById($id);
        if ($info['code'] != 0) {
            return $info;
        }

        if (empty($info['data']['form_json'])) {
            return dataReturn(-1, '请先完成设计');
        }

        // 生成菜单
        $this->makeMenu($info['data']);

        $subTableMap = json_decode($info['data']['sub_table_key'], true);
        $newColumnMap = [];
        $column2type = [];
        $column2title = [];
        $formJson = json_decode($info['data']['form_json'], true);
        $subTable = [];
        $subTableKey = [];

        // 分析column
        if (isset($formJson['column'])) {
            foreach ($formJson['column'] as $vo) {

                // 子表单
                if ($vo['type'] == 'dynamic') {
                    $subTable[] = $vo;
                    continue;
                }

                $newColumnMap[] = $vo['prop'];
                $column2type[$vo['prop']] = $vo['type'];
                $column2title[$vo['prop']] = $vo['label'];
            }
        }

        // 分析group
        if (isset($formJson['group'])) {
            foreach ($formJson['group'] as $k => $v) {

                foreach ($v['column'] as $vo) {

                    // 子表单
                    if ($vo['type'] == 'dynamic') {
                        $subTable[] = $vo;
                        continue;
                    }

                    $newColumnMap[] = $vo['prop'];
                    $column2type[$vo['prop']] = $vo['type'];
                    $column2title[$vo['prop']] = $vo['label'];
                }
            }
        }

        try {

            // 生成主表
            $this->makeMainTable($info, $type, $newColumnMap, $column2title, $column2type);

            // 生成子表
            foreach ($subTable as $key => $vo) {

                $subNewColumnMap = [];
                $subColumn2title = [];

                foreach ($vo['children']['column'] as $v) {
                    $subNewColumnMap[] = $v['prop'];
                    $subColumn2title[$v['prop']] = $v['label'];
                }

                $info['data']['title'] = $vo['label'];

                if (isset($subTableMap[$vo['prop']])) {
                    // 老表还是用原来的key 防止错乱
                    $subTableKey[$vo['prop']] = $subTableMap[$vo['prop']];
                    $tableKey = $subTableMap[$vo['prop']];
                    unset($subTableMap[$vo['prop']]);
                } else {
                    $subTableKey[$vo['prop']] = $key;
                    $tableKey = $key;
                }

                $this->makeSubTable($info, $type, $subNewColumnMap, $subColumn2title, $tableKey);
            }

            if (!empty($subTableMap)) {
                // 删除移除的附表 TODO 第一版本不考虑保留，直接移除
                foreach ($subTableMap as $vo) {
                    $tableName = makeTable($info['data']['table'] . '_auto_sub_' . $vo);
                    Db::execute('DROP TABLE IF EXISTS `' . $tableName . '`');
                }
            }

            // 累加部署版本号
            Db::name('form')->where('id', $info['data']['id'])->update([
                'status' => 2,
                'update_time' => date('Y-m-d H:i:s'),
                'deploy_version' => $info['data']['deploy_version'] + 1,
                'sub_table_num' => count($subTable),
                'sub_table_key' => json_encode($subTableKey)
            ]);

        } catch (\Exception $e) {

            return dataReturn(-5, '部署失败', $e->getMessage());
        }

        return dataReturn(0, '部署成功');
    }

    public function undeploy($id, $type)
    {
        $info = $this->formModel->getInfoById($id);
        if ($info['code'] != 0) {
            return $info;
        }

        if ($type == 1) {

            $this->dropTable($info['data']);

            $this->formModel->editForm([
                'title' => $info['data']['title'],
                'id' => $info['data']['id'],
                'status' => 1,
                'sub_table_num' => 0,
                'sub_table_key' => ''
            ]);
        } else if ($type == 2) {

            $this->formModel->editForm([
                'title' => $info['data']['title'],
                'id' => $info['data']['id'],
                'status' => 1
            ]);
        }

        // 移除菜单
        $this->nodeModel->removeMenuById($id);

        return dataReturn(0, '卸载成功');
    }

    /**
     * 获取表单信息
     * @param $param
     * @return array
     */
    public function getInfoById($param)
    {
        $id = $param['id'];
        $info = $this->formModel->getInfoById($id);
        $formJson = json_decode($info['data']['form_json'], true);

        $field2Dict = [];
        $header = [];
        if (isset($formJson['column'])) {
            foreach ($formJson['column'] as $vo) {

                // 字表单字段不显示
                if ($vo['type'] == 'dynamic') {
                    continue;
                }

                $header[] = [
                    'label' => $vo['label'],
                    'property' => $vo['prop']
                ];

                // 为了方便字典翻译，优化前端显示
                if (isset($vo['dicData'])) {

                    $dictDataMap = [];
                    foreach ($vo['dicData'] as $k => $v) {
                        $dictDataMap[$v['value']] = $v['label'];
                    }

                    $field2Dict[$vo['prop']] = $dictDataMap;
                }
            }
        }

        // 分析group
        if (isset($formJson['group'])) {
            foreach ($formJson['group'] as $k => $v) {

                foreach ($v['column'] as $vo) {

                    // 字表单字段不显示
                    if ($vo['type'] == 'dynamic') {
                        continue;
                    }

                    $header[] = [
                        'label' => $vo['label'],
                        'property' => $vo['prop']
                    ];
                }
            }
        }

        try {

            $param['queryParams'] = json_decode($param['queryParams'], true);

            if (!empty($param['queryParams']) &&
                !empty($param['queryParams']['condition']) &&
                !empty($param['queryParams']['childTips'])) {

                $where = $this->buildWhere($param['queryParams']['childTips']);
                if ($param['queryParams']['condition'] == 'and') {


                    $data = Db::name($info['data']['table'] . '_auto')->where($where)
                        ->order('id', 'desc')->paginate($param['limit']);
                } else if ($param['queryParams']['condition'] == 'or') {

                    $data = Db::name($info['data']['table'] . '_auto')->whereOr($where)
                        ->order('id', 'desc')->paginate($param['limit']);
                }

            } else {

                $data = Db::name($info['data']['table'] . '_auto')
                    ->order('id', 'desc')->paginate($param['limit']);
            }


            $has = Db::execute('desc `' . config('database.connections.mysql.prefix') .
                $info['data']['table'] . '_auto' . '` `flow_status`');
            if ($has) {
                $header[] = [
                    'label' => '审批状态',
                    'property' => 'flow_status'
                ];
            }

        } catch (\Exception $e) {
            return dataReturn(0, 'success', [
                'header' => $header,
                'data' => []
            ]);
        }

        $flowStatus = config('flow.flow_status');

        // 翻译字段汉字
        $data = $data->each(function($item) use ($field2Dict, $flowStatus) {

            foreach ($item as $key => $vo) {

                if (isset($field2Dict[$key])) {

                    $showValueMap = [];
                    $valueMap = explode(',', $vo);

                    foreach ($valueMap as $valueKey) {
                        $showValueMap[] = isset($field2Dict[$key][$valueKey]) ? $field2Dict[$key][$valueKey] : $valueKey;
                    }

                    $item[$key] = implode(',', $showValueMap);
                }

                if ($key == 'flow_status') {
                    $item[$key] = $flowStatus[$vo];
                }
            }

            return $item;
        });

        return dataReturn(0, 'success', [
            'header' => $header,
            'data' => $data->getCollection(),
            'total' => $data->total()
        ]);
    }

    /**
     * 获取表单设计数据
     * @param $id
     * @return array
     */
    public function getFormDataById($id)
    {
        $info = $this->formModel->getInfoById($id);
        $formJson = json_decode($info['data']['form_json'], true);

        return dataReturn(0, 'success', $formJson);
    }

    /**
     * 获取表单数据
     * @param $param
     * @return array
     */
    public function getDiyDataInfo($param)
    {
        try {
            $info = $this->formModel->getInfoById($param['form_id']);

            $formData = Db::name($info['data']['table'] . '_auto')->where('id', $param['table_id'])->find();
            if (!empty($info['data']['sub_table_key'])) {

                $subTable = json_decode($info['data']['sub_table_key'], true);
                foreach ($subTable as $field => $table) {

                    $formData[$field] = Db::name($info['data']['table'] . '_auto_sub_' . $table)
                        ->where($info['data']['table'] . '_auto_id', $param['table_id'])->select();
                }
            }

            return dataReturn(0, 'success', $formData);
        } catch (\Exception $e) {

            return dataReturn(-1, $e->getMessage());
        }
    }

    /**
     * 添加表单数据
     * @param $param
     * @return array
     */
    public function addDiyData($param)
    {
        Db::startTrans();
        try {

            $id = $param['id'];
            unset($param['id']);

            $info = $this->formModel->where('id', $id)->find();
            // 罗列所有的子表单
            $subTable = json_decode($info['sub_table_key'], true);
            $subTableId = array_keys($subTable);
            $subTableData = [];
            $mainTableData = [];

            foreach ($param as $key => $vo) {
                if (in_array($key, $subTableId)) {

                    $subTableData[$key] = $vo;
                } else {

                    if (is_array($vo)) {
                        $mainTableData[$key] = json_encode($vo);
                    } else {
                        $mainTableData[$key] = $vo;
                    }
                }
            }

            $mainTableData['create_time'] = date('Y-m-d H:i:s');
            $id = Db::name($info['table'] . '_auto')->insertGetId($mainTableData);

            // 处理子表
            foreach ($subTableData as $key => $sub) {

                $subData = [];
                foreach ($sub as $k => $vo)  {
                    foreach ($vo as $field => $v) {
                        if (strpos($field, '$') === 0 || strpos($field, '_') === 0 ) {
                            continue;
                        }

                        $subData[$k][$field] = $v;
                    }

                    $subData[$k]['create_time'] = date('Y-m-d H:i:s');
                    $subData[$k][$info['table'] . '_auto_id'] = $id;
                }

                Db::name($info['table'] . '_auto_sub_' . $subTable[$key])->insertAll($subData);
            }

            Db::commit();
        } catch (\Exception $e) {
            print_r($e->getTraceAsString());
            Db::rollback();
            return dataReturn(-1, $e->getMessage());
        }

        return dataReturn(0, '添加成功', $id);
    }

    /**
     * 编辑表单数据
     * @param $param
     * @return array
     */
    public function editDiyData($param)
    {
        Db::startTrans();
        try {

            if (isset($param['flow_status']) && $param['flow_status'] != 1) {
                return dataReturn(-10, '该业务已经在审核中，无法修改');
            }

            $id = $param['id'];
            $pkId = $param['pk_id'];
            unset($param['id'], $param['pk_id'], $param['uid']);

            $info = $this->formModel->where('id', $id)->find();
            // 罗列所有的子表单
            $subTable = json_decode($info['sub_table_key'], true);
            $subTableId = array_keys($subTable);
            $subTableData = [];
            $mainTableData = [];

            foreach ($param as $key => $vo) {
                if (in_array($key, $subTableId)) {

                    $subTableData[$key] = $vo;
                } else {

                    if (is_array($vo)) {
                        $mainTableData[$key] = json_encode($vo);
                    } else {
                        $mainTableData[$key] = $vo;
                    }
                }
            }

            $mainTableData['update_time'] = date('Y-m-d H:i:s');
            Db::name($info['table'] . '_auto')->where('id', $pkId)->update($mainTableData);

            // 处理子表
            foreach ($subTableData as $key => $sub) {

                $subTableDataIdMap = Db::name($info['table'] . '_auto_sub_' . $subTable[$key])->field('id')
                    ->where($info['table'] . '_auto_id', $pkId)->select();

                $subTableDataIds = [];
                foreach ($subTableDataIdMap as $subId) {
                    $subTableDataIds[$subId['id']] = 1;
                }

                $subData = [];
                foreach ($sub as $k => $vo)  {
                    foreach ($vo as $field => $v) {
                        if (strpos($field, '$') === 0 || strpos($field, '_') === 0 ) {
                            continue;
                        }

                        $subData[$field] = $v;
                    }

                    if (!isset($vo['id'])) {

                        $subData['create_time'] = date('Y-m-d H:i:s');
                        $subData[$info['table'] . '_auto_id'] = $pkId;
                        unset($subData['id']);
                        Db::name($info['table'] . '_auto_sub_' . $subTable[$key])->insert($subData);
                    } else {

                        if (isset($subTableDataIds[$vo['id']])) {
                            unset($subTableDataIds[$vo['id']]);
                        }

                        $subData['update_time'] = date('Y-m-d H:i:s');
                        Db::name($info['table'] . '_auto_sub_' . $subTable[$key])
                            ->where('id', $vo['id'])->update($subData);
                    }


                    // 删除已经被删了的子表单数据
                    if (!empty($subTableDataIds)) {
                        Db::name($info['table'] . '_auto_sub_' . $subTable[$key])
                            ->whereIn('id', array_keys($subTableDataIds))->delete();
                    }
                }
            }

            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            return dataReturn(-1, $e->getMessage());
        }

        return dataReturn(0, '编辑成功', [
            'business_id' => $pkId,
            'flow_res' => []
        ]);
    }

    public function getAllForm()
    {
        return $this->formModel->getAllList(['status' => 2]);
    }

    public function delTableData($param)
    {
        Db::startTrans();
        try {

            $info = $this->formModel->where('id', $param['form_id'])->find();

            $table = $info['table'] . '_auto';
            Db::name($table)->where('id', $param['table_id'])->delete();

            // 罗列所有的子表单
            $subTable = json_decode($info['sub_table_key'], true);

            foreach ($subTable as $sub) {

                Db::name($table . '_sub_' . $sub)->where($info['table'] . '_auto_id', $param['table_id'])->delete();
            }

            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            return dataReturn(-1, $e->getMessage());
        }

        return dataReturn(0, '删除成功');
    }

    /**
     * 构建搜索条件
     * @param childTips
     * @return array
     */
    private function buildWhere($childTips)
    {
        $where = [];

        foreach ($childTips as $vo) {

            switch ($vo['rule']) {

                case 'eq':
                    $where[] = [$vo['field'], '=', $vo['val']];
                    break;
                case 'like':
                    $where[] = [$vo['field'], 'like', '%' . $vo['val'] . '%'];
                    break;
                case 'left_like':
                    $where[] = [$vo['field'], 'like',  '%' . $vo['val']];
                    break;
                case 'right_like':
                    $where[] = [$vo['field'], 'like',  $vo['val'] . '%'];
                    break;
                case 'neq':
                    $where[] = [$vo['field'], '<>',  $vo['val']];
                    break;
                case 'gt':
                    $where[] = [$vo['field'], '>',  $vo['val']];
                    break;
                case 'gte':
                    $where[] = [$vo['field'], '>=',  $vo['val']];
                    break;
                    break;
                case 'lt':
                    $where[] = [$vo['field'], '<',  $vo['val']];
                    break;
                case 'lte':
                    $where[] = [$vo['field'], '<=',  $vo['val']];
                    break;
            }
        }

        return $where;
    }

    /**
     * 生成主表
     * @param $info
     * @param $type
     * @param $newColumnMap
     * @param $column2title
     * @param $column2type
     */
    private function makeMainTable($info, $type, $newColumnMap, $column2title, $column2type)
    {
        $tableName = makeTable($info['data']['table'] . '_auto');
        $hasTable = Db::query('SHOW TABLES LIKE "' . $tableName. '"');
        if (!empty($hasTable)) {
            // 表已经存在了，看用户选则的是何种部署方式
            if ($type == 2) {

                ParseConf::incTable([
                    $newColumnMap,
                    $tableName,
                    $column2title,
                    $column2type
                ]);
            } else {

                ParseConf::coverTable([
                    $newColumnMap,
                    $tableName,
                    $column2title,
                    $column2type,
                    $info['data']['title']
                ]);
            }
        } else {

            ParseConf::makeNewTable([
                $newColumnMap,
                $tableName,
                $column2title,
                $column2type,
                $info['data']['title']
            ]);
        }
    }

    /**
     * 生成子表
     * @param $info
     * @param $type
     * @param $newColumnMap
     * @param $column2title
     * @param $key
     */
    private function makeSubTable($info, $type, $newColumnMap, $column2title, $key)
    {
        $tableName = makeTable($info['data']['table'] . '_auto_sub_' . $key);
        $hasTable = Db::query('SHOW TABLES LIKE "' . $tableName. '"');
        if (!empty($hasTable)) {
            // 表已经存在了，看用户选则的是何种部署方式
            if ($type == 2) {
                ParseConf::incSubTable([
                    $newColumnMap,
                    $tableName,
                    $column2title,
                    $info['data']['table'] . '_auto_id'
                ]);
            } else {
                ParseConf::coverSubTable([
                    $newColumnMap,
                    $tableName,
                    $column2title,
                    $info['data']['title'],
                    $info['data']['table'] . '_auto_id'
                ]);
            }
        } else {

            ParseConf::makeNewSubTable([
                $newColumnMap,
                $tableName,
                $column2title,
                $info['data']['title'],
                $info['data']['table'] . '_auto_id'
            ]);
        }
    }

    private function dropTable($info)
    {
        $tableName = makeTable($info['table'] . '_auto');
        Db::execute('DROP TABLE IF EXISTS `' . $tableName . '`');

        if ($info['sub_table_num'] > 0 && !empty($info['sub_table_key'])) {

            $subTable = json_decode($info['sub_table_key'], true);
            foreach ($subTable as $vo) {
                $tableName = makeTable($info['table'] . '_auto_sub_' . $vo);
                Db::execute('DROP TABLE IF EXISTS `' . $tableName . '`');
            }
        }
    }

    private function makeMenu($param)
    {
        $menu = [
            'node_name' => $param['title'],
            'flag' => 'diy',
            'web_path' => '/design/' . $param['table'],
            'node_path' => '/curd/index?id=' . $param['id'],
            'component' => 'diy_' . $param['id'],
            'node_pid' => 0, // TODO 暂定顶级菜单
            'node_icon' => 'el-icon-cloudy',
            'is_menu' => 2,
            'add_time' => date('Y-m-d H:i:s')
        ];

        $info = $this->nodeModel->addNodeReturnId($menu);
        if ($info['code'] != 0) {
            return $info;
        }

        $operation[] = [
            'node_name' => '新增',
            'flag' => 'diy',
            'web_path' => '',
            'node_path' => '/curd/add?id=' . $param['id'],
            'component' => '',
            'node_pid' => $info['data']['id'],
            'node_icon' => '',
            'is_menu' => 1,
            'add_time' => date('Y-m-d H:i:s')
        ];

        $operation[] = [
            'node_name' => '编辑',
            'flag' => 'diy',
            'web_path' => '',
            'node_path' => '/curd/edit?id=' . $param['id'],
            'component' => '',
            'node_pid' => $info['data']['id'],
            'node_icon' => '',
            'is_menu' => 1,
            'add_time' => date('Y-m-d H:i:s')
        ];

        $operation[] = [
            'node_name' => '删除',
            'flag' => 'diy',
            'web_path' => '',
            'node_path' => '/curd/del?id=' . $param['id'],
            'component' => '',
            'node_pid' => $info['data']['id'],
            'node_icon' => '',
            'is_menu' => 1,
            'add_time' => date('Y-m-d H:i:s')
        ];

        $this->nodeModel->insertAll($operation);
    }

    public function delForm($id)
    {
        return $this->formModel->delById($id);
    }
}